package common.jisc.manager;

import common.jisc.command.Command;
import common.jisc.exception.StopHandlingException;
import common.jisc.exception.UnknownCommandException;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * User: Denis Fahritdinov
 * Date: 10/14/12
 * Time: 4:04 PM
 */
public class DefaultCommandManager implements CommandManager {

    protected static final Logger logger = Logger.getLogger(DefaultCommandManager.class.getSimpleName());

    protected String splitCommandRegex;
    protected boolean isMultipleProcessing;
    protected boolean throwUnknownCommandException;
    protected List<Command> commands;

    public DefaultCommandManager() {
        splitCommandRegex = " ";
        isMultipleProcessing = true;
        throwUnknownCommandException = false;
        commands = new LinkedList<Command>();
    }

    /**
     * Add new command
     *
     * @param command command
     */
    public void addCommand(Command command) {
        if (command == null) {
            return;
        }
        if (commands.contains(command)) {
            return;
        }
        commands.add(command);
    }

    /**
     * Remove command
     *
     * @param command
     */
    public void removeCommand(Command command) {
        if (command == null) {
            return;
        }
        commands.remove(command);
    }

    /**
     * @return command args split regex
     */
    public String getSplitCommandRegex() {
        return splitCommandRegex;
    }

    /**
     * @param splitCommandRegex command args split regex
     */
    public void setSplitCommandRegex(String splitCommandRegex) {
        this.splitCommandRegex = splitCommandRegex;
    }

    /**
     * @return continue handling commands after it has been handling
     */
    public boolean isMultipleProcessing() {
        return isMultipleProcessing;
    }

    /**
     * @param multipleProcessing continue handling commands after it has been handling
     */
    public void setMultipleProcessing(boolean multipleProcessing) {
        isMultipleProcessing = multipleProcessing;
    }

    /**
     * @return throw exception if command was not handled
     */
    public boolean isThrowUnknownCommandException() {
        return throwUnknownCommandException;
    }

    /**
     * @param throwUnknownCommandException throw exception if command was not handled
     */
    public void setThrowUnknownCommandException(boolean throwUnknownCommandException) {
        this.throwUnknownCommandException = throwUnknownCommandException;
    }

    /**
     * Handle user command
     *
     * @param command
     */
    @Override
    public void handle(String command) {
        if (command == null || command.isEmpty()) {
            return;
        }
        String[] tokens = command.split(splitCommandRegex);
        if (tokens == null || tokens.length < 1) {
            return;
        }

        String userCommand = tokens[0].trim();
        String[] args = Arrays.copyOfRange(tokens, 1, tokens.length);
        boolean wasHandled = false;
        try {
            for (Command c : commands) {
                if (c.canHandle(userCommand, args.length)) {
                    wasHandled = true;
                    c.getHandler().handle(userCommand, args);
                    if (!isMultipleProcessing) {
                        break;
                    }
                }
            }
        } catch (StopHandlingException e) {
            String msg = String.format("Stop command \"%s\" handling process", e.getCommand());
            logger.log(Level.WARNING, msg, e);
        }

        if (!wasHandled && throwUnknownCommandException) {
            throw new UnknownCommandException(command);
        }
    }

    /**
     * Generate commands help
     *
     * @return
     */
    public String help() {
        StringBuilder help = new StringBuilder("Supported commands:\n");
        for (Command command : commands) {
            help.append(String.format("\t%s\n", command.toString()));
        }
        return help.toString();
    }
}
